﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Harkon.AppServer.cOne.Service;
using System.Threading;
using log4net;

namespace Harkon.AppServer.cOne.CommandQueues
{
    public abstract class CommandQueueBase<TCommand> : AbstractService, ICommandQueue where TCommand : StandardCommand
    {
        private static ILog logger = LogManager.GetLogger(typeof(CommandQueueBase<TCommand>));
        private bool _running = false;
        private Queue<StandardCommand> _queue;

        public Type CommandType { get { return typeof(TCommand); } }

        public ICommandQueueManager QueueManager { get; set; }

        public CommandQueueBase()
        {
            _queue = new Queue<StandardCommand>();
        }

        protected abstract void CommandExecution(StandardCommand command);

        #region Worker thread region

        private void Run(object state)
        {
            Thread.CurrentThread.Name = typeof(TCommand).Name + "Queue";
            while (_running)
            {
                bool waiting = false;
                StandardCommand command = null;
                lock (_queue)
                {
                    waiting = _queue.Count != 0;
                    if (waiting)
                    {
                        command = _queue.Dequeue();
                    }
                }
                if (!waiting)
                {
                    logger.Debug("Empty queue -> wait for pulse ");
                    Monitor.Enter(this);
                    Monitor.Wait(this);
                    Monitor.Exit(this);
                }
                else
                {
                    logger.DebugFormat("Forwarding {0}", command);
                    CommandExecution(command);
                }
            }
        }

        #endregion

        #region ICommandQueue methods

        public void EnqueueCommand(StandardCommand command)
        {
            lock (_queue)
            {
                logger.DebugFormat("Enqueuing {0}", command);
                _queue.Enqueue(command);
            }
            Monitor.Enter(this);
            Monitor.Pulse(this);
            Monitor.Exit(this);
        }

        #endregion

        #region Lifecycle methods

        public override bool Init()
        {
            if (base.Init())
            {
                _running = true;

                //Retrieve QueueManager from ServiceRegistry
                ServiceName queueManagerName = new ServiceName("CommandQueueManager", "CommandQueues", "Harkon.AppServer.cOne.CommandQueues.ICommandQueueManager");
                QueueManager = ServiceRegistry.GetService(queueManagerName) as ICommandQueueManager;
                QueueManager.RegisterCommandQueue(this);
                return true;
            }
            return false;
        }

        public override bool Start()
        {
            if (base.Start())
            {
                ThreadPool.QueueUserWorkItem(Run);
                return true;
            }
            return false;
        }

        public override bool Stop()
        {
            if (base.Stop())
            {
                _running = false;
                Monitor.Enter(this);
                Monitor.Pulse(this);
                Monitor.Exit(this);
                return true;
            }
            return false;
        }

        #endregion

        public override string ToString()
        {
            return string.Format("[Queue for command type={0}]", typeof(TCommand).Name);
        }
    }
}
